package tdx

import (
	"fmt"
	"gitee.com/quant1x/data/cache"
	"gitee.com/quant1x/data/category"
	"gitee.com/quant1x/data/category/trading"
	"gitee.com/quant1x/gotdx/quotes"
	"gitee.com/quant1x/pandas"
	"gitee.com/quant1x/pandas/stat"
	"github.com/mymmsc/gox/api"
	"github.com/mymmsc/gox/logger"
	"github.com/mymmsc/gox/progressbar"
	"strconv"
	"strings"
)

const (
	kTransactionFirstTime = "09:25" // 第一个时间
	kTransactionLastTime  = "15:00" // 最后一个时间
)

var (
	kTransactionRawFields = []string{"Time", "Price", "Vol", "Num", "BuyOrSell"}
	kTransactionFields    = []string{"time", "price", "vol", "num", "buyorsell"}
)

// GetTickAll 下载全部tick数据
func GetTickAll(code string) (vol float64) {
	tdxApi := prepare()
	marketId, marketName, code := category.DetectMarket(code)
	code = marketName + code
	info, err := tdxApi.GetFinanceInfo(marketId, code, 1)
	if err != nil {
		return
	}
	tStart := strconv.FormatInt(int64(info.IPODate), 10)
	fixStart := cache.TickStartDate
	if tStart < fixStart {
		tStart = fixStart
	}
	tEnd := cache.Today()
	logger.Errorf("[%s]tick数据范围: %s<->%s", code, tStart, tEnd)
	dateRange := trading.TradeRange(tStart, tEnd)
	// 反转切片
	dateRange = stat.Reverse(dateRange)
	bar := progressbar.NewBar(2, fmt.Sprintf("同步[%s]", code), len(dateRange))
	today := dateRange[0]
	ignore := false
	for _, tradeDate := range dateRange {
		bar.Add(1)
		if ignore {
			continue
		}
		fname := cache.TickFilename(code, tradeDate)
		if tradeDate != today && cache.FileIsValid(fname) {
			// 如果已经存在, 假定之前的数据已经下载过了, 不需要继续
			ignore = true
			continue
		}
		df := GetTickData(code, tradeDate)
		if df.Nrow() == 0 && tradeDate != today {
			// 如果数据为空, 且不是当前日期, 认定为从这天起往前是没有分笔成交数据的
			if logger.IsDebug() {
				logger.Debugf("[%s]tick数据[%s<->%s]空, 后面忽略", code, tradeDate, today)
			}
			ignore = true
		} else if df.Nrow() > 0 && tradeDate == today {
			VOLUME := df.Col("vol").DTypes()
			PRICE := df.Col("price").DTypes()
			if len(VOLUME) > 0 {
				price := PRICE[0]
				vol = VOLUME[0]

				shIndex := api.StartsWith(code, []string{"sh00", "sh99", "sh88"})
				szIndex := strings.HasPrefix(code, "sz399")
				if shIndex || szIndex {
					vol = (vol * 100) / price
				} else {
					// 个股股换算成股
					vol = vol * 100
				}
			}
		}
	}

	return
}

// 获取指定日期的分笔成交记录
// K线附加成交数据
func tickData(code string, date string, ignorePreviousData bool) pandas.DataFrame {
	var df pandas.DataFrame
	if ignorePreviousData {
		// 在默认日期之前的数据直接返回空
		startDate := cache.CorrectDate(cache.TickStartDate)
		if date < startDate {
			return df
		}
	}
	startTime := kTransactionFirstTime
	skipCount := 0
	filename := cache.GetTickFilename(code, date, false)
	if cache.FileExist(filename) {
		df = pandas.ReadCSV(filename)
		if df.Nrow() > 0 {
			lastTime := df.Col("time").IndexOf(-1).(string)
			if lastTime == kTransactionLastTime {
				return df
			}
			times := stat.Reverse(df.Col("time").Strings())
			firstTime := ""
			for _, tm := range times {
				if len(firstTime) == 0 {
					firstTime = tm
					startTime = firstTime
					skipCount++
					continue
				}
				if tm < firstTime {
					startTime = firstTime
					break
				} else {
					skipCount++
				}
			}
			// 截取 startTime之前的记录
			df = df.Subset(0, df.Nrow()-skipCount)
		}
	}

	tdxApi := prepare()
	marketId, _, code := category.DetectMarket(code)
	offset := uint16(quotes.TDX_TRANSACTION_MAX)
	// 只求增量, 分笔成交数据是从后往前取数据, 缓存是从前到后顺序存取
	start := uint16(0)
	date = cache.CorrectDate(date)
	history := make([]quotes.TickTransaction, 0)
	hs := make([]quotes.TransactionReply, 0)
	for {
		var data *quotes.TransactionReply
		var err error
		if trading.CurrentlyTrading(date) {
			data, err = tdxApi.GetTransactionData(marketId, code, start, offset)
		} else {
			iDate := stat.AnyToInt64(date)
			data, err = tdxApi.GetHistoryTransactionData(marketId, code, uint32(iDate), start, offset)
		}

		if err != nil {
			panic(err)
		}
		if data == nil || data.Count == 0 {
			break
		}
		var tmp quotes.TransactionReply
		tmpList := stat.Reverse(data.List)
		for _, td := range tmpList {
			// 追加包含startTime之后的记录
			if td.Time >= startTime {
				tmp.Count += 1
				tmp.List = append(tmp.List, td)
			}
		}
		tmp.List = stat.Reverse(tmp.List)
		hs = append(hs, tmp)
		if tmp.Count < offset {
			// 已经是最早的记录
			// 需要排序
			break
		}
		start += offset
	}
	hs = stat.Reverse(hs)
	for _, v := range hs {
		history = append(history, v.List...)
	}
	if len(history) == 0 {
		return df
	}

	incremental := pandas.LoadStructs(history)
	incremental = incremental.Select(kTransactionRawFields)
	err := incremental.SetNames(kTransactionFields...)
	if err != nil {
		return pandas.DataFrame{}
	}
	df = df.Concat(incremental)

	return df
}

// GetTickData 获取指定日期的分笔成交记录
func GetTickData(code string, date string) pandas.DataFrame {
	df := tickData(code, date, false)
	if df.Nrow() == 0 {
		return df
	}
	tickFile := cache.TickFilename(code, date)
	err := cache.CheckFilepath(tickFile)
	if err != nil {
		return pandas.DataFrame{}
	}
	err = df.WriteCSV(tickFile)
	if err != nil {
		return pandas.DataFrame{}
	}

	return df
}

// 附加成交量
func attachVolume(df pandas.DataFrame, code string) pandas.DataFrame {
	dates := df.Col("date").Strings()
	if len(dates) == 0 {
		return df
	}
	buyVolumes := []stat.DType{}
	sellVolumes := []stat.DType{}
	buyAmounts := []stat.DType{}
	sellAmounts := []stat.DType{}
	for _, tradedate := range dates {
		tmp := tickData(code, tradedate, true)
		buyVolume, sellVolume, buyAmount, sellAmount := inflowCount(tmp)
		buyVolumes = append(buyVolumes, buyVolume)
		sellVolumes = append(sellVolumes, sellVolume)
		buyAmounts = append(buyAmounts, buyAmount)
		sellAmounts = append(sellAmounts, sellAmount)
	}
	bv := pandas.NewSeries(stat.SERIES_TYPE_DTYPE, "bv", buyVolumes)
	sv := pandas.NewSeries(stat.SERIES_TYPE_DTYPE, "sv", sellVolumes)
	ba := pandas.NewSeries(stat.SERIES_TYPE_DTYPE, "ba", buyAmounts)
	sa := pandas.NewSeries(stat.SERIES_TYPE_DTYPE, "sa", sellAmounts)
	df = df.Join(bv, sv, ba, sa)
	return df
}

// 统计内外盘
func inflowCount(df pandas.DataFrame) (buyVolume, sellVolume, buyAmount, sellAmount stat.DType) {
	if df.Nrow() > 0 {
		for i := 0; i < df.Nrow(); i++ {
			m := df.IndexOf(i)
			t := stat.AnyToInt32(m["buyorsell"])
			p := stat.AnyToFloat64(m["price"])
			v := stat.AnyToFloat64(m["vol"])

			buy := false
			var buyOffset, sellOffset int64
			if t == quotes.TDX_TICK_BUY {
				buy = true
			} else if t == quotes.TDX_TICK_SELL {
				buy = false
			}
			if t == quotes.TDX_TICK_NEUTRAL {
				vn := int64(v)
				buyOffset = vn / 2
				sellOffset = vn - buyOffset

				// 买入
				buyVolume += float64(buyOffset)
				buyAmount += float64(buyOffset) * p * 100
				// 卖出
				sellVolume += float64(sellOffset)
				sellAmount += float64(sellOffset) * p * 100
			} else if !buy {
				// 卖出
				sellVolume += v
				sellAmount += v * p * 100
			} else if buy {
				// 买入
				buyVolume += v
				buyAmount += v * p * 100
			}
		}
	}
	return
}
